﻿#region Header
//************************************************************************************
// Name: ConcreteNHibernateDao
// Description: ConcreteNHibernateDao
// Created On:  02-Aug-2011
// Created By:  Swathi
// Last Modified On:
// Last Modified By:
// Last Modified Reason:
//*************************************************************************************
#endregion Header
using System;
using System.Collections.Generic;
using Base.Data;
using NHibernate;
using Base.Common;
using Base.Utils.Attributes;
using NHibernate.Criterion;
using Base.Data;

namespace Base.Data
{
    public abstract class AbstractNHibernateDao<T, IdT> : IDao<T, IdT>
    {
       
        /// <summary>
        /// Loads an instance of type T from the DB based on its ID.
        /// </summary>
        public T GetById(IdT id, bool shouldLock)
        {
            T entity;

            if (shouldLock)
            {
                entity = (T)NHibernateSession.Load(persitentType, id, LockMode.Upgrade);
            }
            else
            {
                entity = (T)NHibernateSession.Get(persitentType, id);
            }
            return entity;
        }

        /// <summary>
        /// Loads every instance of the requested type with no filtering.
        /// </summary>
        public List<T> GetAll()
        {
            //Base.Utils.Logger.Instance.LoggingHelper.Log(persitentType.Name + " ***&&&*** " + "Getting Database", LoggingLevels.Info);
            return GetByCriteria();
        }

        /// <summary>
        /// Loads every instance of the requested type using the supplied <see cref="ICriterion" />.
        /// If no <see cref="ICriterion" /> is supplied, this behaves like <see cref="GetAll" />.
        /// </summary>
        public List<T> GetByCriteria(params ICriterion[] criterion)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
            criteria.SetCacheable(IsCacheable());
            foreach (ICriterion criterium in criterion)
            {
                criteria.Add(criterium);
            }

            return criteria.List<T>() as List<T>;
        }

        public List<T> GetByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
            Example example = Example.Create(exampleInstance);

            foreach (string propertyToExclude in propertiesToExclude)
            {
                example.ExcludeProperty(propertyToExclude);
            }

            criteria.Add(example);

            return criteria.List<T>() as List<T>;
        }

        /// <summary>
        /// Looks for a single instance using the example provided.
        /// </summary>
        /// <exception cref="NonUniqueResultException" />
        public T GetUniqueByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            List<T> foundList = GetByExample(exampleInstance, propertiesToExclude);

            if (foundList.Count > 1)
            {
                throw new NonUniqueResultException(foundList.Count);
            }

            if (foundList.Count > 0)
            {
                return foundList[0];
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// For entities that have assigned ID's, you must explicitly call Save to add a new one.
        /// See http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-id-assigned.
        /// </summary>
        public T Save(T entity)
        {
            NHibernateSession.Save(entity);
            OnSaved(entity, SaveAction.Insert);
            return entity;
        }

        /// <summary>
        /// For entities with automatatically generated IDs, such as identity, SaveOrUpdate may 
        /// be called when saving a new entity.  SaveOrUpdate can also be called to _update_ any 
        /// entity, even if its ID is assigned.
        /// </summary>
        public T SaveOrUpdate(T entity)
        {
            NHibernateSession.SaveOrUpdate(entity);
            OnSaved(entity, SaveAction.Update);
            return entity;
        }

        public void Delete(T entity)
        {
            NHibernateSession.Delete(entity);
            OnSaved(entity, SaveAction.Delete);
        }

        /// <summary>
        /// Commits changes regardless of whether there's an open transaction or not
        /// </summary>
        public void CommitChanges()
        {
            if (NHibernateSessionManager.Instance.HasOpenTransactionOn(SessionFactoryConfigPath))
            {
                NHibernateSessionManager.Instance.CommitTransactionOn(SessionFactoryConfigPath);
            }
            else
            {
                // If there's no transaction, just flush the changes
                NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath).Flush();
            }
        }

        /// <summary>
        /// Exposes the ISession used within the DAO.
        /// </summary>
        protected ISession NHibernateSession
        {
            get
            {
                return NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath);
            }
        }

        private Type persitentType = typeof(T);
        protected string SessionFactoryConfigPath {get{return NHutils.GetHibernateConfigFileNameWithPath();}}

        #region IDao<T,IdT> Members


        public void OnSaved(T entity, SaveAction action)
        {
            if (Saved != null)
                Saved.Invoke(entity, null);
        }

        #endregion

        #region IDao<T,IdT> Members


        public event EventHandler<EventArgs> Saved;

        #endregion

        private bool IsCacheable()
        {
            bool result = false;
            NHibernateCacheAttribute cacheAttrib = default(NHibernateCacheAttribute);
            foreach (object obj in persitentType.GetCustomAttributes(false))
            {
                cacheAttrib = obj as NHibernateCacheAttribute;
                if (cacheAttrib != null)
                {
                    return cacheAttrib.Cacheable;
                }
            }
            return result;
        }
    }

}